%{
#include <stdlib.h>
#include <stdio.h>
#include "treenode.h"

#define _STDLIB_H

extern int yylex();

void yyerror(const char* err)
{
    fprintf(stderr,"error: %s\n", err);
}

int yywrap()
{
    return 1;
}

%}
%union {
    TreeNode* tree;
}

%defines

%token <tree> ID EQUALS "==" NOT_EQUALS "!=" INCR "++" DECR "--"
%token <tree> INCR_ASSIGN "+=" DECR_ASSIGN "-="
%token <tree> STRING_LITERAL TICK_STRING_LITERAL 
%token <tree> INTEGER_LITERAL HEX_LITERAL CHAR_LITERAL 
%token <tree> FLOAT_LITERAL OCT_LITERAL
%token <tree> NEW "new" TTRUE "true" TFALSE "false" MODULE "module"
%token <tree> INDENT DEDENT UNMATCHED_INDENT NEWLINE
%token <tree> SCOPE "**" UPSCOPE "->"

%token <tree> ASSIGN LIST HASH_ITEM FUNC CALL UPSCOPE_EXPR STMT
%type <tree> start interactiveInput statementList list listItemList hashList
%type <tree> hashItemList hashItem upscopeExpression upscopeAccessor 
%type <tree> simpleStatement compoundStatement functionDecl statement
%type <tree> suite suiteStatementList functionCall paramList expression
%type <tree> literalExpression assignmentStatement
%%

start
    : /* empty */                { $$ = NULL; }
    | start interactiveInput     { $$ = NULL; TreeNode_delete($2); }
    | start INDENT interactiveInput DEDENT
                                 { $$ = NULL; TreeNode_delete($3); }
    ;

interactiveInput
    : NEWLINE                     { $$ = TreeNode_new2(STMT); }
    | statementList NEWLINE       { $$ = $1; }
    | compoundStatement
    ;

statementList
    : ';'                          { $$ = TreeNode_new2(LIST); }
    | simpleStatement              { $$ = TreeNode_new3(LIST, $1, NULL); }
    | statementList ';'
    | statementList ';' simpleStatement 
                                   { TreeNode_addChild($1, $3); $$ = $1; }
    ;

list
    : '[' ']'                      { $$ = TreeNode_new2(LIST); }
    | '[' listItemList ']'         { $$ = $2; }
    ;

listItemList
    : expression                    { $$ = TreeNode_new3(LIST, $1, NULL); }
    | listItemList ',' expression   { TreeNode_addChild($1, $3); $$ = $1; }
    ;

hashList
    : '{' '}'                       { $$ = TreeNode_new2(LIST); }
    | '{' hashItemList '}'          { $$ = $2; }
    ;

hashItemList
    : hashItem                      { $$ = TreeNode_new3(LIST, $1, NULL); }
    | hashItemList ',' hashItem     { TreeNode_addChild($1, $3); $$ = $1; }
    ;

hashItem
    : ID ':' expression             { $$ = TreeNode_new3(HASH_ITEM, $1, $3, NULL); }
    ;

upscopeExpression
    : upscopeAccessor               { $$ = TreeNode_new2(UPSCOPE_EXPR); }
    | upscopeAccessor '.' ID        { $$ = TreeNode_new3(UPSCOPE_EXPR, $1, $3, NULL); }
    ;

upscopeAccessor
    : "**" 
    | upscopeAccessor "->"          { TreeNode_addChild($1, $2); $$ = $1; }
    ;

simpleStatement
    : assignmentStatement
    ;

compoundStatement
    : functionDecl
    ;

functionDecl
    : ID '(' ')' ':' suite          { $$ = TreeNode_new3(FUNC, $1, $5, NULL); }
    | ID '(' paramList ')' ':' suite 
				    { $$ = TreeNode_new3(FUNC, $1, $3, $6, NULL); }
    ;

statement
    : statementList NEWLINE
    | compoundStatement
    ;

suite 
    : statementList NEWLINE         { $$ = $1; }
    | NEWLINE INDENT suiteStatementList DEDENT
                                    { $$ = $3; }
    ;

suiteStatementList
    : statement                     { $$ = TreeNode_new2(LIST); }
    | suiteStatementList statement  { TreeNode_addChild($1, $2); $$ = $1; }
    ;

functionCall 
    : ID '(' ')'                    { $$ = TreeNode_new3(CALL, $1, NULL); }
    | ID '(' paramList ')'          { $$ = TreeNode_new3(CALL, $1, $3, NULL); }
    ;
    
paramList
    : expression                    { $$ = TreeNode_new2(LIST); }
    | paramList ',' expression      { TreeNode_addChild($1, $3); $$ = $1; }
    ;

expression
    : ID                  
    | functionCall        
    | literalExpression   
    | list                
    | hashList            
    | upscopeExpression   
    ;

literalExpression
    : STRING_LITERAL      
    | INTEGER_LITERAL     
    | FLOAT_LITERAL       
    | HEX_LITERAL         
    | CHAR_LITERAL        
    | TICK_STRING_LITERAL 
    ;

assignmentStatement
    : ID '=' expression             { $$ = TreeNode_new3(ASSIGN, $1, $3, NULL); }
    ;

%%

